package com.gqs.bookstore.dao;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//抽象类只能被继承，不能被修改
public abstract class BaseDao {
	private static final String DRIVERCLASS = "com.mysql.jdbc.Driver";
	private static final String URL = "jdbc:mysql://localhost:3306/bookstore?characterEncoding=utf8";
	private static final String UID = "root";
	private static final String PWD = "root";
	private Connection conn = null;
	
	static {
		try {
			Class.forName(DRIVERCLASS);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 打开连接
	 * @throws SQLException
	 */
	private void openConnection() throws SQLException{
		if(this.conn==null || this.conn.isClosed()){
			this.conn = DriverManager.getConnection(URL, UID, PWD);
		}
	}
	/**
	 * 关闭连接
	 * @throws SQLException
	 */
	protected void closeConnection() throws SQLException{
		if(this.conn!=null && !this.conn.isClosed()){
			this.conn.close();
		}
	}
	
	/***************************************真正干活的增删改查*************************************/
	/**
	 * 这里用eclipse将重复的代码抽取出来重新构成一个通用的方法
	 * 建立连接，获得带主键因子的pstat[如果参数isGenerateKey传进来的值是true的话]
	 * @param sql
	 * @param params
	 * @return
	 * @throws SQLException
	 */
	private PreparedStatement buildPreparedStatement(String sql, boolean isGenerateKey,
			Object[] params) throws SQLException {
		this.openConnection();
		PreparedStatement pstat=
					isGenerateKey?conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS):
								  conn.prepareStatement(sql);
		for(int i=0;i<(params==null?0:params.length); i++){
			pstat.setObject(i+1, params[i]);//索引是从1开始的
		}
		return pstat;
	}
	
	/**
	 * 
	 * @param sql
	 * @param isGenerateKey
	 * @param params
	 * @return 自动生成的主键
	 * @throws SQLException
	 * -----定义成包装类Long
	 * 功能-----获取主键的自动增长因子并且插入数据，但是这里处理成只返回主键id值
	 */
	protected Long GetGenerateIncreaseId(String sql, Object[] params) throws SQLException{
		PreparedStatement pstat = this.buildPreparedStatement(sql, true, params);
		pstat.executeUpdate();//执行插入操作
		ResultSet rs=pstat.getGeneratedKeys();//获取由于执行此 PreparedStatement 对象而创建的所有自动生成的主键
		Long id =null;//只有定义成包装类才能赋值为null
		if(rs.next()){
			id=rs.getLong(1);//获取所有自动生成的建的当前键值
		}
		return id;
	}
	
	/**
	 * 不带参数的查询【即sql语句里就用了拼字符串】
	 * @param sql
	 * @return
	 * @throws SQLException
	 */
//	public ResultSet executeQuery(String sql) throws SQLException{
//		this.openConnection();
//		PreparedStatement pstat= conn.prepareStatement(sql);
//		return pstat.executeQuery();
//	}
	
	/**
	 *  执行单查或者全查
	 * 带参数的查询【即sql语句里用?占位符代替拼字符串】
	 * @param sql
	 * @param params
	 * @return  List中的每个元素表示一行数据，是一个Map
	 * 		    map中的key 是列名  value是这一列的值
	 * 			select c_id id,c_name name from t_users
	 * 			如果有结果，那么 list中的一个map的就可能是这样：
	 *    key:id  	value:1
	 *    key:name  value:xxx
	 * @throws SQLException
	 */
	
	protected List<Map<String, Object>> executeQuery(String sql, Object[] params) throws SQLException{
		PreparedStatement pstat = this.buildPreparedStatement(sql, false, params);
	    ResultSet rs = pstat.executeQuery();
	    //取出列的名字
	    ResultSetMetaData rsColNames=rs.getMetaData();//取出所有列的名字--获取此 ResultSet 对象rs的列的编号、类型和属性。
	    //获得列数
	    int columnCounts = rsColNames.getColumnCount();//获得当前第id行共有几列的列数---返回此 ResultSet 对象rs中的列数[固定]。 
	    //这里的思路是先把数据库里的数据取出来装到list里面，然后再在对应实现查找的方法里将list里的东西取出来，最后在set进对象里
	  	//最终前台用对象的get()方法展现出来
	    List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
	    //while完成一次循环后，将一行记录加到list里
	    while(rs.next()){//如果rs返回一行记录，则rs只在第一行停留；否则rs游标指向n多行
	    	Map<String, Object> record = new HashMap<String, Object>();//List中的每个元素表示一行数据，是一个Map
	    	for (int i = 0; i < columnCounts; i++) {//for循环在当前while循环里只在一行记录里将 键值对赋值给一行记录的map
	    		//rsColNames.getColumnLabel();这里实际上是给每一列的列名编号，键和值的索引都是从1开始的---获取用于打印输出和显示的指定列的建议标题。
				//record.put(String key, Object value)------key名字和值value
	    		record.put(rsColNames.getColumnLabel(i+1), rs.getObject(i+1));
			}
	    	list.add(record);
	    }
		return list;
	}
	
	/**
	 * 不带参数的增删改【即sql语句里就用了拼字符串】
	 * @param sql
	 * @return
	 * @throws SQLException
	 */
//	public int executeUpdate(String sql) throws SQLException{
//		this.openConnection();
//		PreparedStatement pstat = conn.prepareStatement(sql);
//		return pstat.executeUpdate();
//	}
	
	/**
	 * 带参数的增删改【即sql语句里用?占位符代替拼字符串】
	 * @param sql
	 * @param params
	 * @return
	 * @throws SQLException
	 */
	protected Integer executeUpdate(String sql, Object...params) throws SQLException{
		PreparedStatement pstat = this.buildPreparedStatement(sql,false, params);
		return pstat.executeUpdate();
	}
	
}
